﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using QY.Util;
using System.Text.RegularExpressions;


namespace Loong.Net
{
    public class HttpClient : Loong.Net.IHttpClient
    {
        private HttpWebRequest request;
        private String rspText;
        private MIMEPayload mimePayload;
        private CookieManager cookies;
        private String postVars;//存储Post数据
        private HTTPMethod method;
        private bool isRedirect=false;

        ////基本HTTP header定义///
        private WebHeaderCollection headers;
        private int timeout = 30000;
        private string referer;
        private string userAgent ="Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0)";
        private string accept = " */*";
        private string AcceptLanguage="zh-cn";
        private string AcceptEncoding = "gzip, deflate";


        //代理定义//
        private static IWebProxy proxy = null;

        private const int STREAM_BUFFER_SIZE = 2048;

        private static Dictionary<String, String> replacementHosts = new Dictionary<String, String>();

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="url">目标地址</param>
        /// <param name="method">HTTP请求方法</param>
        public HttpClient(string url, HTTPMethod method)
        {
            this.Url = url;
            this.Method = method;
            this.cookies = new CookieManager();
        }


        public void SendRequest()
        {
            if (!string.IsNullOrEmpty(referer))
            {
                request.Referer = referer;
            }

            if (request.ServicePoint != null)
            {
                request.ServicePoint.Expect100Continue = false;
            }


            if (proxy != null)
            {
                request.Proxy = proxy;
            }           

                    

            //配置请求头信息
            request.ProtocolVersion = HttpVersion.Version11;
            request.KeepAlive = true;
            request.AllowAutoRedirect = isRedirect;
            request.ReadWriteTimeout = timeout;
            request.Timeout = timeout;//30秒超时
            request.Accept = accept;
            request.UserAgent = userAgent;     
            request.Headers["Accept-Language"] = AcceptLanguage;
            request.Headers["Accept-Encoding"] = AcceptEncoding;      


            using (Stream s = this.GetResponseStream())
            {
                // SendRequestAndGetResponseStream sets rspText if necessary
                if (this.rspText == null)
                {
                    using (StreamReader sr = new StreamReader(s,Encoding.Default))
                    {
                        this.rspText = sr.ReadToEnd();
                    }
                   // Console.WriteLine(this.Url);
                }
            }
          
        }

        /// <summary>
        /// 引用页面地址
        /// </summary>
        public string Referer
        {
            get { return this.referer; }
            set { this.referer=value; }
        }

        /// <summary>
        /// 允许格式
        /// </summary>
        /// 
        public string Accept
        {
            get { return this.accept; }
            set { this.accept = value; }
        }

        /// <summary>
        /// 客户端环境、浏览器信息
        /// </summary>
        public string UserAgent
        {
            get { return this.userAgent; }
            set { this.userAgent = value; }
        }

        /// <summary>
        /// 是否使用代理
        /// </summary>
        public IWebProxy Proxy
        {
            get { return proxy; }
            set { proxy = value; }
        }

        /// <summary>
        /// 是否允许同定向
        /// </summary>
        public bool IsRedirect
        {
            get { return this.isRedirect; }
            set { this.isRedirect = value; }

        }

        /// <summary>
        /// 取消当前请求
        /// </summary>
        public void CancelRequest()
        {
            if (request != null)
            {
                request.Abort();
            }
        }

        public CookieManager Cookies
        {
            get { return this.cookies; }
            set { this.cookies = value; }
        }

        public WebHeaderCollection Headers
        {
            get { return this.headers; }
            set { this.headers = value; }
        }

        /// <summary>
        /// 获取或设置HTTP的Method;
        /// </summary>
        public HTTPMethod Method
        {
            get { return this.method;}
            set {
                this.method = value;
                switch (value)
                {
                    case HTTPMethod.Get:
                        {
                            request.Method = HTTPMethod.Get.ToString();
                            break;

                        }
                    case HTTPMethod.Post:
                        {
                            mimePayload = null;
                            postVars = "";
                            request.Method = HTTPMethod.Post.ToString();
                            request.ContentType = "application/x-www-form-urlencoded";
                            break;
                        }
                    case HTTPMethod.MultipartPost:
                        {
                            mimePayload = new MIMEPayload();
                            postVars = null;
                            request.Method = HTTPMethod.Post.ToString();
                            request.ContentType = "multipart/form-data; boundary=" + mimePayload.Boundary;
                            break;
                        }
                }
            }
        }

        /// <summary>
        /// 目标地址
        /// </summary>
        public string Url
        {
            get
            {
                if (request == null)
                {
                    return null;
                }
                return request.RequestUri.OriginalString;
            }
            set
            {

                if (string.IsNullOrEmpty(value))
                {
                    throw new Exception("目标地址不能为空!");
                }

                //if (!Regular.IsURL(value))
                //{
                //    throw new Exception("输入地址格式不正确!");
                //}

                //创建一个web请求对象
                ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
                
                request = (HttpWebRequest)WebRequest.Create(value);
            }
        }

        public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            //直接确认，否则打不开
            return true;
        }


        /// <summary>
        /// 获取或设置超时时间，单位毫秒。
        /// </summary>
        public int Timeout
        {
            get { return this.timeout; }
            set { this.timeout = value; }
        }


        public Stream GetResponseStream()
        {
            
            if (this.request == null)
            {
                throw new InvalidOperationException("请先设置请示目标地址!");
            }
            if (this.rspText != null)
            {
                throw new InvalidOperationException("不能重复请求");
            }

            // Add previously stored cookies to the header
            this.cookies.SetCookies(request);

            // Create the request payload
            byte[] data = null;

            if (this.method==HTTPMethod.Get)
            {
                data = new byte[0];
            }

            if (this.method ==HTTPMethod.Post && mimePayload == null)
            {
                data = Encoding.Default.GetBytes(postVars);
            }

            if (this.method == HTTPMethod.Post && mimePayload != null)
            {
                mimePayload.Finish();
                data = mimePayload.Data;
            }

            //当Method为Post且有提交值时
            if (data.Length > 0)
            {
                request.ContentLength= data.Length;
                Stream st = request.GetRequestStream();
                st.Write(data, 0, data.Length);
                st.Close();
            }

            using (HttpWebResponse rsp = (HttpWebResponse)request.GetResponse())
            {
                this.cookies.GetCookies(rsp);

               
                 // 重定向处理
                //String location = rsp.Headers["Location"];
                //if (location != null && location != this.Url.ToString())
                //{
                //    Uri oldUri = new Uri(this.Url);
                //    Uri newUri;
                //    try
                //    {
                //        newUri = new Uri(location);
                //    }
                //    catch (UriFormatException)
                //    {
                //        // The new location is probably a relative URL
                //        newUri = new Uri(oldUri, location);
                //    }

                //    if (oldUri.AbsolutePath == newUri.AbsolutePath)
                //    {
                //        // Path is the same, this is a host name change;
                //        // therefore cache the change
                //        lock (replacementHosts)
                //        {
                //            replacementHosts[oldUri.Host] = newUri.Host;
                //        }

                //        // Redirect
                //        SetTarget(newUri.ToString(), HT .Get);
                //        return this.SendRequestAndGetResponseStream();
                //    }
                //    else
                //    {
                //        // Asked for redirect without the same path,
                //        // let the caller handle all details
                //        this.SetTarget(newUri.ToString(), HTTPRequestType.Get);
                //        this.rspText = "<script>window.location.href = \"" + newUri.ToString() + "\"</script>";
                //    }
                //}

                // Get the original response stream and the decoder response stream
                Stream responseStream = rsp.GetResponseStream();
                Stream decoderStream = null;
                if (rsp.ContentEncoding == "gzip")
                {
                    decoderStream = new GZipInputStream(rsp.GetResponseStream());
                }
                else if (rsp.ContentEncoding == "deflate")
                {
                    decoderStream = new InflaterInputStream(rsp.GetResponseStream());
                }
                else
                {
                    decoderStream = responseStream;
                }

                using (responseStream)
                {
                    using (decoderStream)
                    {
                        MemoryStream decodedStream = new MemoryStream();
                        byte[] buffer = new byte[STREAM_BUFFER_SIZE];

                        while (true)
                        {
                            int readBytes = decoderStream.Read(buffer, 0, STREAM_BUFFER_SIZE);
                            if (readBytes > 0)
                            {
                                decodedStream.Write(buffer, 0, readBytes);
                            }
                            else
                            {
                                break;
                            }
                        }

                        decodedStream.Seek(0, SeekOrigin.Begin);
                        return decodedStream;
                    }
                }
            }


        }

        /// <summary>
        /// 提交Post数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void AddPostData(string key, string value)
        {
            value = Uri.EscapeUriString(value);

            if (postVars != null)
            {
                postVars += ((postVars.Length > 0) ? "&" : "") + key + ((value != null) ? "=" + value : "");
            }
            else if (mimePayload != null)
            {
                mimePayload.AddValue(key, value);
            }
            else
            {
                throw new Exception("如果你加入Post 数据,要将Method设置为Post或MultipartPost!");
            }
        }

        /// <summary>
        /// 提交文件,上传
        /// </summary>
        /// <param name="key"></param>
        /// <param name="fileName"></param>
        public void AddPostFile(String key, String fileName)
        {
            if (mimePayload != null)
            {
                mimePayload.AddFile(key, fileName);
            }
            else
            {
                throw new Exception("如果你加入Post 数据,要将Method设置为Post或MultipartPost!");
            }
        }

        /// <summary>
        /// 返回请求内容
        /// </summary>
        public String ResponseText
        {
            get
            {
                if (this.rspText == null)
                {
                    this.SendRequest();
                }
                return this.rspText;
            }
        }


        /// <summary>
        /// Class for handling certificates in frameworks that have trouble
        /// handling it in some cases (Mono, mostly) which accepts all
        /// certificates. That might be a dangerous thing to do.
        /// </summary>
        private class AcceptAllPolicy : ICertificatePolicy
        {
            public AcceptAllPolicy()
            { }
            public enum CertificateProblem : long
            {
                CertEXPIRED = 0x800B0101,
                CertVALIDITYPERIODNESTING = 0x800B0102,
                CertROLE = 0x800B0103,
                CertPATHLENCONST = 0x800B0104,
                CertCRITICAL = 0x800B0105,
                CertPURPOSE = 0x800B0106,
                CertISSUERCHAINING = 0x800B0107,
                CertMALFORMED = 0x800B0108,
                CertUNTRUSTEDROOT = 0x800B0109,
                CertCHAINING = 0x800B010A,
                CertREVOKED = 0x800B010C,
                CertUNTRUSTEDTESTROOT = 0x800B010D,
                CertREVOCATION_FAILURE = 0x800B010E,
                CertCN_NO_MATCH = 0x800B010F,
                CertWRONG_USAGE = 0x800B0110,
                CertUNTRUSTEDCA = 0x800B0112
            }
            public static bool DefaultValidate = false; 

           

            public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest request, int problem)
            {

                bool ValidationResult = false;
                Console.WriteLine("Certificate Problem with accessing " +  request.RequestUri);
                Console.Write("Problem code 0x{0:X8},", (int)problem);
                Console.WriteLine(GetProblemMessage((CertificateProblem)problem));

                ValidationResult = DefaultValidate;
                return true;
                //return ValidationResult;
            }

            private String GetProblemMessage(CertificateProblem Problem)
            {
                String ProblemMessage = "";
                CertificateProblem problemList = new CertificateProblem();
                String ProblemCodeName = Enum.GetName(problemList.GetType(), Problem);
                if (ProblemCodeName != null)
                    ProblemMessage = ProblemMessage + "-Certificateproblem:" +
                       ProblemCodeName;
                else
                    ProblemMessage = "Unknown Certificate Problem";
                return ProblemMessage;
            }


        }




    }
    
}
